package mth.masterimp;

import java.io.FileWriter;
import java.io.IOException;

public class AlphaWeightsCalculation {
	/* 
	 * Diese Klasse dient der Berechnung der Gewichte. 
	 * Es werden die Varianzen und Standardabweichungen ermittelt und anschließend
	 * die Gewichtungen berechnet.
	 * Die Bezeichnung für double[][] variances ist irreführend, da hier bereits die 
	 * Standardabweichung gespeichert wird.
	 */
	private int[][] pairs;
	private int pairCount;
	private double[][] variances;//variance for every attribute per neighborhood
	private double[] weights; //local final weight for every attribute
	private double[][] neighborhoodWeights; //local weights for every attribute per neighborhood
	private Neighborhood[] neighborhoods;
	private DataRepresentator dr;
	private int countOfAllMembers;
	
	public AlphaWeightsCalculation(Neighborhood[] n, int[][] p, int pc) {
		pairCount = pc;
		pairs = new int[2][pairCount];
		for (int a=0; a<pairCount; a++) {
			pairs[0][a]=p[0][a];
			pairs[1][a]=p[1][a];
		}
		
		neighborhoods=n;
		dr = new DataRepresentator();
		weights = new double[dr.getValueMatrix().getNumberOfColumns()-dr.getValueMatrix().getNumberOfSpecialColumns()];
		variances = new double[neighborhoods.length][weights.length];
		neighborhoodWeights = new double[neighborhoods.length][weights.length];
		
		countOfAllMembers=0;
		for (int a=0; a<neighborhoods.length; a++) {
			countOfAllMembers+=neighborhoods[a].getMember().length;
		}
	}
	
	public void applyWeights() {
		dr.updateValueMatrix(weights);
	}
	
	private void calculateNeighborhoodWeights(int i) {
		//this is more easy to handle
		for (int a=0; a<variances[i].length; a++) {
			neighborhoodWeights[i][a]=Math.sqrt(1/variances[i][a]);
		}
	}
	
	public void calculateWeights() {
		updateAllLocalVariances();
		for (int a=0; a<neighborhoods.length; a++) {
			calculateNeighborhoodWeights(a);
		}
		
		for (int a=0; a<weights.length; a++) {
			for (int b=0; b<neighborhoods.length; b++	) {
				weights[a]+=((neighborhoodWeights[b][a]*neighborhoods[b].getMember().length)/countOfAllMembers);
			}
		}
		double maxWeight=0.0;
		int maxWeightPos=0;
		for (int a=0; a<weights.length; a++	) {
			if (weights[a]>maxWeight && weights[a] != Double.POSITIVE_INFINITY) {
				maxWeight=weights[a];
				maxWeightPos=a;
			}
		}
		for (int a=0; a<weights.length; a++) {
			if (weights[a]==Double.POSITIVE_INFINITY) {
				weights[a]=weights[maxWeightPos]*2;
			} else if (weights[a]==Double.NEGATIVE_INFINITY) {
				weights[a]=0;
			}
		}
	}
	
	public double[] getWeights() {
		return weights;
	}
	
	public String getWeightsAsString() {
		String wstring="";
		for (int a=0; a<weights.length; a++) {
			wstring+=String.valueOf(weights[a])+","+String.valueOf(weights[a]);
			if (a<weights.length-1)
				wstring+=",";
		}
		wstring+="\n";
		return wstring;
	}
	
	public void print(String file) {
		String outputString="";
		
//		//variances
//		for (int a=0; a<variances.length; a++) {
//			outputString+="Varianzen für Nachbarschaft "+a+"\n";
//			for (int b=0; b<variances[a].length; b++) {
//				if (b==variances[a].length-1) {
//					outputString+=variances[a][b]+"\n";
//				} else {
//					outputString+=variances[a][b]+",";
//				}
//				outputString+="\n";
//			}
//		}
//		try {
//			FileWriter fw = new  FileWriter(file+"_variances", false);
//			fw.write(outputString);
//			fw.close();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//		
//		//weights of neighborhoods
//		outputString="";
//		for (int a=0; a<neighborhoodWeights.length; a++) {
//			outputString+="Gewichte der Nachbarschaft "+a+"\n";
//			for (int b=0; b<neighborhoodWeights[a].length; b++) {
//				if (b==neighborhoodWeights[a].length-1) {
//					outputString+=neighborhoodWeights[a][b]+"\n";
//				} else {
//					outputString+=neighborhoodWeights[a][b]+",";
//				}
//				outputString+="\n";
//			}
//		}
//		try {
//			FileWriter fw = new  FileWriter(file+"_weights_local", false);
//			fw.write(outputString);
//			fw.close();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
		
		//global weights
		outputString="";
		for (int a=0; a<weights.length; a++) {
			outputString+=weights[a];
			if (a==weights.length-1) {
				outputString+="\n";
			} else {
				outputString+=",";
			}
		} 
		try {
			FileWriter fw = new  FileWriter(file+"_weights_global", false);
			fw.write(outputString);
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void updateAllLocalVariances() {
		for (int a=0; a<neighborhoods.length; a++) {
			variances[a]=updateLocalVariance(neighborhoods[a], a); 
			//sending "a" to avoid loss of contact between neighborhood and variance[a]
		}
	}
	
	private double[] updateLocalVariance(Neighborhood n, int v) {
		double sumOfValues=0.0;
		double[] vreturn = new double[variances[v].length];
		if (n.getMember().length==1) {
			for (int a=0; a<vreturn.length; a++) {
				vreturn[a]=1.0;
			}
		} else {
			for (int a=0; a<dr.getValueMatrix().getNumberOfColumns()-dr.getValueMatrix().getNumberOfSpecialColumns(); a++) {
				for (int b=0; b<n.getMember().length; b++) {
					sumOfValues+=dr.getValueMatrix().getRow(n.getMember()[b]).getValue(a);
				}
				double mean=sumOfValues/n.getMember().length;
				for (int b=0; b<n.getMember().length; b++) {
					vreturn[a]+=((dr.getValueMatrix().getRow(n.getMember()[b]).getValue(a)-mean) *
							(dr.getValueMatrix().getRow(n.getMember()[b]).getValue(a)-mean));
				}
				vreturn[a]=vreturn[a]/n.getMember().length;
			}
		}
		return vreturn;
	}
	
	public void useWeights() {
		dr.updateValueMatrix(weights);
	}
	
}
